//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//     Filename: LMRXMLTranslator.cpp                                       //
//  Create date: February 15, 2005                                          //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//  Description: This file implements the LMRXMLTranslator class.  This     //
//                class is a protocol translator that translates from the   //
//                RTG Let 'em Ride protocol to XML and back.                //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2005 - Realtime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <stdio.h>
#include "lib.h"
#include "LMRXMLTranslator.h"
#include "XMLDoc.h"
#include "endian.h"
#include "log.h"
#include "card.h"

//////////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////////
LMRXMLTranslator::LMRXMLTranslator( Server* pServer, Connection* pParent) : 
                                             XMLTranslator( pServer, pParent, 9, 1 )
{
}

//////////////////////////////////////////////////////////////////////////////
// Destructor
//////////////////////////////////////////////////////////////////////////////
LMRXMLTranslator::~LMRXMLTranslator()
{
}

/////////////////////////////////////////////////////////////////////////////
// TranslateIncomingMessage
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateIncomingMessage( uint8* pGameInBuff,  
												  uint32 nGameInBuffSize, 
												  uint8* pOutBuff, 
												  uint32 nOutBuffSize )
{
    bool8 bRetVal = false;

	// Handle the messages.  Note, I'm only going to look at the first character
	//  in a small attempt to optimize a bit
	switch( m_sMsgID[0] )
	{
		case 'L':
			bRetVal = TranslateInLogin( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		case 'B':
			bRetVal = TranslateInBalance( m_pDoc, pGameInBuff, nGameInBuffSize, GetNumBytesMsgLen() );
			break;

        case 'D':
			bRetVal = TranslateInDeal( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

        case 'C':
			bRetVal = TranslateInContinue( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

        case 'R':
			bRetVal = TranslateInRaise( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		case 'J':
			bRetVal = TranslateInJackpot( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		default:
			String sMsg = TEXT("Error, received unknown client message: ");
			sMsg += m_sMsgID;
			Log::msgError( sMsg );
			break;
	}

	// Report Error
	if( !bRetVal )
	{
		if ( m_sMsgID[0] == 'L' )
		{
			// Set an error into the out buffer
			ReturnLoginError( pOutBuff, nOutBuffSize );
		}
		else
		{
			// Set an error into the out buffer
			ReturnErrorMessage( TEXT("ServerError"), pOutBuff, nOutBuffSize );

			String sMsg = TEXT("Error translating incoming message: ");
			sMsg += m_sMsgID;
			Log::msgError( sMsg );
		}
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutgoingMessage
//////////////////////////////////////////////////////////////////////////////
uint32 LMRXMLTranslator::TranslateOutgoingMessage( uint8* pOutBuff, 
												   uint32 nOutBuffSize, 
												   uint8* pGameOutBuff, 
												   uint32 nGameOutBuffSize, 
												   uint32 nTotalGameBytes    )
{
	bool8 bResult = true;
	uint32 nGameBytesUsed = 0;
	uint32 nOutBytesUsed = 0;

	while ( bResult && (nGameBytesUsed < nTotalGameBytes) )
	{
		// Keep track of buffers as we might be processing more than one game
		//   message at a time.
		uint8* pCurGameBuf     = pGameOutBuff     + nGameBytesUsed;
		uint32 nCurGameBufSize = nGameOutBuffSize - nGameBytesUsed;
		uint8* pCurOutBuf      = pOutBuff         + nOutBytesUsed;
		uint32 nCurOutBufSize  = nOutBuffSize     - nOutBytesUsed;

		// Make sure buffer has not been overrun!
		if ( nOutBytesUsed > nOutBuffSize )
		{
			ASSERT(false);
			Log::msgError( "Error, out buffer overflow!" );
			return 0;
		}

		// Now the actual message handlers!
		switch( pCurGameBuf[1] )
		{
			case 'L':
				bResult = TranslateOutLogin( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

            case 'm':
				bResult = TranslateOutBalance( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize, 1 );
				break;

            case 'D':
				bResult = TranslateOutDeal( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

            case 'O':
				bResult = TranslateOutT1Done( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'J':
				bResult = TranslateOutJackpot( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'F':
				bResult = TranslateOutFinished( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'E':
				bResult = TranslateGenericOutError( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize, 1 );
				break;

			case 'G':
			case 'N':
			case 'B':
			case '$':
			case '!':
				bResult = TranslateOutError( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			default:
				bResult = ReturnErrorMessage( TEXT("UnknownMessage"), pCurOutBuf, nCurOutBufSize );
				Log::msgError( "Received unknown game message: %c", pCurGameBuf[1] );
				break;
		}

		// Check and see if any errors occurred
		if ( !bResult )
		{
			ReturnErrorMessage( TEXT("ParseError"), pCurOutBuf, nCurOutBufSize );
			Log::msgError( "Error translating outgoing message: %c", pCurGameBuf[1] );
		}

		// We know that the size of the message is always stored in the first
		//  byte of the client message.
		nGameBytesUsed += pCurGameBuf[0];

		// Out XML Messages are supposed to be NULL Terminated
		nOutBytesUsed += strlen( (char*) pCurOutBuf );
		nOutBytesUsed++;  // Add one for the NULL

		DEBUGMSG( ("\n***Outgoing: %s", pCurOutBuf) );
	}

	return nOutBytesUsed;
}

//****************************************************************************
// Incoming
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
// TranslateInLogin
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateInLogin( XMLDoc* pDoc, 
                                          uint8*  pGameInBuff, 
                                          uint32  nGameInBuffSize )
{
    ////////////////////////////////////////////////////////////////
	// Login the user
	////////////////////////////////////////////////////////////////
    if ( !LoginUser( pDoc ) )
    {
        return false;
    }
    
    // Start by skipping the first byte of the message, that will be the
	//  length
	uint32 nOffset = 1;

	////////////////////////////////////////////////////////////////
	// Write Login for CS
	////////////////////////////////////////////////////////////////
	pGameInBuff[nOffset++] = 'M';
	pGameInBuff[nOffset++] = 'L';
	pGameInBuff[nOffset++] = 'L';
	pGameInBuff[nOffset++] = 'R';

	////////////////////////////////////////////////////////////////
	// Add Version Info
	////////////////////////////////////////////////////////////////
	if ( !AddLoginVersionInfo( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Add ID
	////////////////////////////////////////////////////////////////
	if ( !AddLoginID( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}
    	
	////////////////////////////////////////////////////////////////
	// play for money/play for fun
	////////////////////////////////////////////////////////////////
	if ( !AddLoginMoneyType( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Cient type: CSN-7534
	////////////////////////////////////////////////////////////////
	if ( !AddClientID( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Authentication token. CSN-7075: New Authentication
	////////////////////////////////////////////////////////////////
	if ( !AddAuthenticationToken( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Finally, add message size
	////////////////////////////////////////////////////////////////
	pGameInBuff[0] = (uint8) nOffset;

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInDeal
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateInDeal( XMLDoc* pDoc, 
                                         uint8*  pGameInBuff, 
                                         uint32  nGameInBuffSize )
{
	// Message Size
	pGameInBuff[0] = 10;

	// Message ID
	pGameInBuff[1] = 'D';

	// Bet Amount
	uint32 nBetAmount;
	if ( !pDoc->GetSingleNodeLong( TEXT("//bet"), nBetAmount ) )
	{
		return false;
	}
	Endian::SendInt4( nBetAmount, pGameInBuff + 2 );

	// Progressive Bet Amount
	if ( !pDoc->GetSingleNodeLong( TEXT("//prog_bet"), nBetAmount ) )
	{
		return false;
	}
	Endian::SendInt4( nBetAmount, pGameInBuff + 6 );

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInContinue
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateInContinue( XMLDoc* pDoc, 
                                             uint8*  pGameInBuff, 
                                             uint32  nGameInBuffSize )
{
	// Message Size
	pGameInBuff[0] = 2;

    // Get Turn Number
	uint8 nTurn;
	if ( !pDoc->GetSingleNodeByte( TEXT("//turn"), nTurn ) )
	{
		return false;
	}

	// Message ID
    if ( nTurn == 1 )
    {
     	pGameInBuff[1] = 'C';
    }
    else
    {
        ASSERT( nTurn == 2 );
    	pGameInBuff[1] = 'c';
    }

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInRaise
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateInRaise( XMLDoc* pDoc, 
                                          uint8*  pGameInBuff, 
                                          uint32  nGameInBuffSize )
{
	// Message Size
	pGameInBuff[0] = 2;

    // Get Turn Number
	uint8 nTurn;
	if ( !pDoc->GetSingleNodeByte( TEXT("//turn"), nTurn ) )
	{
		return false;
	}

	// Message ID
    if ( nTurn == 1 )
    {
     	pGameInBuff[1] = 'R';
    }
    else
    {
        ASSERT( nTurn == 2 );
    	pGameInBuff[1] = 'r';
    }

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInJackpot
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateInJackpot( XMLDoc* pDoc, 
                                            uint8*  pGameInBuff, 
                                            uint32  nGameInBuffSize )
{
	// Message Size
	pGameInBuff[0] = 2;

	// Message ID
	pGameInBuff[1] = 'J';

	return true;
}

//****************************************************************************
// Outgoing
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
// TranslateOutLogin
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateOutLogin( uint8* pOutBuff, 
										   uint32 nOutBuffSize, 
										   uint8* pGameOutBuff, 
										   uint32 nGameOutBuffSize )
{
	XMLDoc doc;
    Card c;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Login") ) )
	{
		return false;
	}

	// Keep track of offset in buffer
	uint32 nOffset = 2;

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Minimum Bet
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("min_bet"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Maximum Bet
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("max_bet"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Pay table
	uint8 nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("pay_table"), (uint32) nVal ) )
	{
		return false;
	}

	// Prog pay table
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("prog_pay_table"), (uint32) nVal ) )
	{
		return false;
	}
    
    // Progressive on
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("prog_on"), (bool8) nVal ) )
	{
		return false;
	}
	
	// Saved game flag
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("saved"), (bool8) nVal ) )
	{
		return false;
	}
	
	// If we have a saved game, add data
	if ( nVal )
	{
		if ( !doc.AddRootChildNode( TEXT("saved_game") ) )
		{
			return false;
		}

		// Bet
		Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
		if ( !doc.AddChildNode( TEXT("//saved_game"), TEXT("bet"), (uint32) nValue ) )
		{
			return false;
		}
		nOffset += 4;

		// Progressive Bet
		Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
		if ( !doc.AddChildNode( TEXT("//saved_game"), TEXT("prog_bet"), (uint32) nValue ) )
		{
			return false;
		}
		nOffset += 4;

        // Turn 1 done flag
		nVal = pGameOutBuff[nOffset++];
        if ( nVal )
        {
		    // Dealer Card
		    nOffset += c.deSerialize( pGameOutBuff + nOffset );
		    if ( !doc.AddChildNode( TEXT("//saved_game"), TEXT("dealer_card"), c ) )
		    {
			    return false;
		    }        

            // Raised Flag
		    nVal = pGameOutBuff[nOffset++];
		    if ( !doc.AddChildNode( TEXT("//saved_game"), TEXT("raised"), (bool8) nVal ) )
		    {
			    return false;
		    }
        }

		// Num Cards
		uint8 nNumCards = pGameOutBuff[nOffset++];
		if ( !doc.AddChildNode( TEXT("//saved_game"), TEXT("num_cards"), (uint32) nNumCards ) )
		{
			return false;
		}

		// Add each card
		for( uint8 i = 0; i < nNumCards; i++ )
		{
			// Get Card value
			nOffset += c.deSerialize( pGameOutBuff + nOffset );
			
			// Build node string
			String sNode;
			sNode.sprintf( TEXT("card_%d"), i );			

			// Add XML
			if ( !doc.AddChildNode( TEXT("//saved_game"), sNode, c ) )
			{
				return false;
			}
		}

        // Pay Table
		nVal = pGameOutBuff[nOffset++];
		if ( !doc.AddChildNode( TEXT("//saved_game"), TEXT("pay_table"), (uint32) nVal ) )
		{
			return false;
		}
	}

	if ( !AddCurrencyOutput(&doc) )	// CSN-11204: return currency
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutDeal
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateOutDeal( uint8* pOutBuff, 
									      uint32 nOutBuffSize, 
										  uint8* pGameOutBuff, 
										  uint32 nGameOutBuffSize )
{
	XMLDoc doc;
	Card c;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Deal") ) )
	{
		return false;
	}

	// Keep track of offset in buffer
	uint32 nOffset = 2;

	// Num Cards
	uint8 nNumCards = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("num_cards"), (uint32) nNumCards ) )
	{
		return false;
	}

	// Add each card
	for( uint8 i = 0; i < nNumCards; i++ )
	{
		// Get Card value
		nOffset += c.deSerialize( pGameOutBuff + nOffset );

		// Build node string
		String sNode;
		sNode.sprintf( TEXT("card_%d"), i );

		// Add XML
		if ( !doc.AddRootChildNode( sNode, c ) )
		{
			return false;
		}
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutDeal
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateOutT1Done( uint8* pOutBuff, 
									        uint32 nOutBuffSize, 
										    uint8* pGameOutBuff, 
										    uint32 nGameOutBuffSize )
{
	XMLDoc doc;
	Card c;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Turn1Done") ) )
	{
		return false;
	}

	// Keep track of offset in buffer
	uint32 nOffset = 2;

	// Dealer Card
	nOffset += c.deSerialize( pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("dealer_card"), c ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutJackpot
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateOutJackpot( uint8* pOutBuff, 
										     uint32 nOutBuffSize, 
									         uint8* pGameOutBuff, 
										     uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Jackpot") ) )
	{
		return false;
	}

	// Check message length
	ASSERT( pGameOutBuff[0] == 10 );

	// Progressive Jackpot
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff + 2 );
	if ( !doc.AddRootChildNode( TEXT("jackpot"), (uint32) nValue ) )
	{
		return false;
	}

	// Ms per one cent increase
	Endian::ReceiveInt4( &nValue, pGameOutBuff + 6 );
	if ( !doc.AddRootChildNode( TEXT("increase"), (uint32) nValue ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutFinished
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateOutFinished( uint8* pOutBuff, 
										      uint32 nOutBuffSize, 
										      uint8* pGameOutBuff, 
										      uint32 nGameOutBuffSize )
{
	XMLDoc doc;
    Card c;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Finished") ) )
	{
		return false;
	}

	// Keep track of offset in buffer
	uint32 nOffset = 2;

	// Dealer's 2nd Card
	nOffset += c.deSerialize( pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("dealer_card"), c ) )
	{
		return false;
	}

	// Ante Payout 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("ante_payout"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// 1st Raise Payout 
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("raise_payout_1"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

    // 2nd Raise Payout 
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("raise_payout_2"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Progressive Payout 
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("prog_payout"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;

	// Player Hand Rank 
	uint8 nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("player_hand_rank"), (uint32) nVal ) )
	{
		return false;
	}
	
	// Outcome
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("outcome"), (uint32) nVal ) )
	{
		return false;
	}
	
	// Balance 
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nOffset );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}
	nOffset += 4;
		
	// Progressive on
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("prog_on"), (bool8) nVal ) )
	{
		return false;
	}

    // Pay table
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("pay_table"), (uint32) nVal ) )
	{
		return false;
	}

	// Prog pay table
	nVal = pGameOutBuff[nOffset++];
	if ( !doc.AddRootChildNode( TEXT("prog_pay_table"), (uint32) nVal ) )
	{
		return false;
	}
	
	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutError
//////////////////////////////////////////////////////////////////////////////
bool8 LMRXMLTranslator::TranslateOutError( uint8* pOutBuff, 
										   uint32 nOutBuffSize, 
										   uint8* pGameOutBuff, 
										   uint32 nGameOutBuffSize )
{
	bool8 bRetVal = false;

	if ( pGameOutBuff[1] == '$' )
	{
		bRetVal = ReturnErrorMessage( TEXT("InsufficientFunds"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == '!' )
	{
		bRetVal = ReturnErrorMessage( TEXT("Shutdown"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'B' )
	{
		bRetVal = ReturnErrorMessage( TEXT("InvalidBet"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'N' )
	{
        // kharmon.  02-21-07.  MCA #29819.  Need to send back different messages for auto and admin logoffs.
        String strErrorName = "InvalidAction";

        if(pGameOutBuff[0]==6)  // The extra 4 bytes have the error code.
        {
            int32 nErrorCode=0;
            Endian::ReceiveInt4( &nErrorCode, &pGameOutBuff[2] );

            switch(nErrorCode)
            {
                case TrackSQL::AdminLogout:
                {
                    strErrorName = "AdminLogout";
                    break;
                }
                case TrackSQL::AutoLogout:
                {
                    strErrorName = "TimedOut";
                    break;
                }
            }            
        }

        bRetVal = ReturnErrorMessage( strErrorName, pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'G' )
	{
		bRetVal = ReturnErrorMessage( TEXT("InProgress"), pOutBuff, nOutBuffSize );
	}

	return bRetVal;
}